/******************************************************************************
** 公司名称：天津柯迪斯科技有限公司
** 版权信息：
** 文件名称： App_Protocol_BF_UART.c
** 模块名称： 八方串口协议处理模块
** 版 本 号： V1.0
** 作    者： 蒲磊
** 
** 修改记录： 版本     修改人      时间         修改内容
**           V1.1    蒲磊      2022.5.23   
**           V1.2    蒲磊      2023.2.16     1.增加了校准应答，且应答函数中增加了40ms的延时（为了适配串口工装）
******************************************************************************/
 
/******************************************************************************
* 头文件
******************************************************************************/
#include <string.h>
#include "Config.h"
#include "App_Protocol_BF_UART.h"
#include "App_MainTask.h"
#include "App_Parameter.h"
/******************************************************************************
* 本文件宏定义 ('#define')                             
******************************************************************************/
#define FRAME_LEN_MAX		    50		/*最大帧长度*/
#define RECEIVE_STATE           0
#define SEND_STATE              1
/******************************************************************************
* 本文件类型定义 ('typedef')                                         
******************************************************************************/

/******************************************************************************
* 本文件静态函数声明 ('static')
******************************************************************************/
static void vPROTOCOL_Factory_Command(void);
static uint8_t ucPROTOCOL_Send_Data(void);
/******************************************************************************
* 全局变量定义 
******************************************************************************/
uint8_t gucRx_Buffer[FRAME_LEN_MAX] = {0};
uint8_t gucTx_Buffer[7] = {0};
uint8_t 					gucRx_Frame_OK 	    = 0;
uint16_t 					gusRx_Len 		    = 0;
stRECEIVED_DATA			    gtReceived_Data 	= {0};
stSEND_DATA				    gtSend_Data			= {0};

uint8_t					    gucTimeOutError 	= 0;
uint8_t                     gucState            = 0;/*状态变量，用于标记当前处于发送状态还是接收状态*/
uint32_t                    gulCommunicationCnt = 0;
/*****************************************************************************
* 函数实现 - global ('extern') and local ('static')
******************************************************************************/
 
 
/******************************************************************************
** 功  能：获取一帧协议数据
** 参  数：pData-指向数据的指针，Len-数据长度
** 返回值：无
** 备  注：该函数是协议处理模块对外获取数据的接口，接收到完整一帧后，调用该函数进行处理
******************************************************************************/
void vPROTOCOL_Get_OneFrame(uint8_t *pData,uint16_t Len)
{
	memcpy(gucRx_Buffer,pData,Len);		/*将数据拷贝到缓冲区*/
	gusRx_Len = Len;					/*数据长度*/
	gucRx_Frame_OK = 1;					/*接收到一帧标志置1*/
}

/******************************************************************************
** 功  能：解析协议数据
** 参  数：无
** 返回值：无
** 备  注：该函数需要循环调用。用于解析接收到的协议数据和定时发送数据
******************************************************************************/
void vPROTOCOL_Loop(void)
{
	
    static uint8_t lucLastCmd;
    static uint8_t lucNoAnswerCnt = 0;
    
	if(gucRx_Frame_OK == 1)
	{
		gulCommunicationCnt = ulBsp_Getticks();
		gucTimeOutError = 0;
		if(gucRx_Buffer[0] == 0x3A && gucRx_Buffer[1] == 0x1C)
		{
			/*内部控制指令*/
			vPROTOCOL_Factory_Command();
            gucState = RECEIVE_STATE;
            lucNoAnswerCnt = 0;
		}
		else
		{
			/*调用八方的协议进行解析*/
            if(gucState == RECEIVE_STATE)
            {
                switch(lucLastCmd)
                {
                    case READ_SOC:
                    {
                        lucNoAnswerCnt = 0;
                        if(gucRx_Buffer[0] == gucRx_Buffer[1])
                        {
                            gtReceived_Data.Soc = gucRx_Buffer[0];
                            gucState = SEND_STATE;/*转发送状态，发送下一个命令*/
                        } 
                        break;
                    }
                    case READ_CURRENT:
                    {
                        lucNoAnswerCnt = 0;
                        if(gucRx_Buffer[0] == gucRx_Buffer[1])
                        {
                            gtReceived_Data.Current = gucRx_Buffer[0];
                            gucState = SEND_STATE;/*转发送状态，发送下一个命令*/
                        }
                        break;
                    }
                    case READ_SPEED:
                    {
                        lucNoAnswerCnt = 0;
                        if(gucRx_Buffer[2] == (uint8_t)(0x20 + gucRx_Buffer[0] + gucRx_Buffer[1]))
                        {
                            gtReceived_Data.Rpm = ((uint16_t)gucRx_Buffer[0] << 8) | gucRx_Buffer[1];
                            gucState = SEND_STATE;/*转发送状态，发送下一个命令*/
                        } 
                        break;
                    }
                    case READ_ERROR:
                    {
                        lucNoAnswerCnt = 0;
                        gtReceived_Data.ErrCode = gucRx_Buffer[0];
                        gucState = SEND_STATE;/*转发送状态，发送下一个命令*/   
                        break;
                    }
                    case 0x00:
                    {
                        break;
                    }
                }
            }
		}
		gucRx_Frame_OK = 0;
	}
	else
	{
        /*通讯超时时间，10s*/
        if(ulBsp_Getticks() - gulCommunicationCnt > 10000)
        {
#if (DEBUG == 0)
            gucTimeOutError = 1;
#endif         
        }
        
        if(++lucNoAnswerCnt > 2)
        {
            lucNoAnswerCnt = 0;
            gucState = SEND_STATE;
        }
	}
    
    /*发送状态*/
    if(gucState == SEND_STATE)
    {
        lucLastCmd = ucPROTOCOL_Send_Data();
        gucState = RECEIVE_STATE;
    }
}

/******************************************************************************
** 功  能：设置大灯状态
** 参  数：State-大灯状态。0-关闭，1-打开
** 返回值：无
** 备  注：所有协议通用
******************************************************************************/
void vPROTOCOL_Set_Lamp(uint8_t State)
{
	gtSend_Data.Lamp = State == 0 ? 0 : 1;
}

/******************************************************************************
** 功  能：设置档位范围
** 参  数：Pas_Range-档位值范围，3，4，5，9
** 返回值：无
** 备  注：本函数不对档位值的合理性做检查。请保证传入的档位值正确。
******************************************************************************/
void vPROTOCOL_Set_Pas_Range(int8_t Pas_Range)
{
	gtSend_Data.Pas_Range = Pas_Range;
}

/******************************************************************************
** 功  能：设置档位
** 参  数：Pas-档位值，最大为9，最小为-1,-1表示助推档位
** 返回值：无
** 备  注：本函数不对档位值的合理性做检查。请保证传入的档位值正确。

******************************************************************************/
void vPROTOCOL_Set_Pas(int8_t Pas)
{
	gtSend_Data.Pas = Pas;
}

/******************************************************************************
** 功  能：设置限速值
** 参  数：Speed-限速值
** 返回值：无
** 备  注：本函数不对传入值的合理性做检查。请保证传入的值正确。
******************************************************************************/
void vPROTOCOL_Set_Speed_Limit(uint16_t Speed)
{
	gtSend_Data.Speed_Limit = Speed;
}

/******************************************************************************
** 功  能：设置轮径
** 参  数：Wheel-轮径值（英寸）
** 返回值：无
** 备  注：本函数不对传入值的合理性做检查。请保证传入的值正确。
**         所有协议通用
******************************************************************************/
void vPROTOCOL_Set_Wheel(uint16_t Wheel)
{
	gtSend_Data.Wheel_Diameter = Wheel;
}






/******************************************************************************
** 功  能：设置发送配置数据
** 参  数：无
** 返回值：无
** 备  注：本函数运行后，即开始发送配置数据。直到再次设置发送运行数据或停止
**         所有协议通用
******************************************************************************/
void vPROTOCOL_Send_Config_Data(void)
{
	
}

/******************************************************************************
** 功  能：设置发送一次配置数据
** 参  数：无
** 返回值：无
** 备  注：本函数运行后，只发送一次配置数据。然后会转为发送运行数据
**         所有协议通用
******************************************************************************/
void vPROTOCOL_Send_Config_Data_OneTime(void)
{
	
}


/******************************************************************************
** 功  能：设置发送运行数据
** 参  数：无
** 返回值：无
** 备  注：八方协议无用。
******************************************************************************/
void vPROTOCOL_Send_Run_Data(void)
{
	
}

/******************************************************************************
** 功  能：停止发送数据
** 参  数：无
** 返回值：无
** 备  注：本函数运行后，即停止发送数据。直到再次设置发送配置数据或发送运行数据
**         所有协议通用
******************************************************************************/
void vPROTOCOL_Send_Stop(void)
{
	
}

/******************************************************************************
** 功  能：获取电池容量
** 参  数：无
** 返回值：-1表示未获取到电池容量，电池容量，0-100%
** 备  注：
******************************************************************************/
int8_t cPROTOCOL_Get_Soc(void)
{
	return gtReceived_Data.Soc;
}

/******************************************************************************
** 功  能：获取当前电流值
** 参  数：无
** 返回值：实际电流值的10倍。比如实际电流12.5A，返回125
******************************************************************************/
uint16_t usPROTOCOL_Get_Current(void)
{
	if(gucTimeOutError == 1)
	{
		return 0;
	}
	else
	{
		return gtReceived_Data.Current * 5;
	}
}



/******************************************************************************
** 功  能：获取当前车速
** 参  数：无
** 返回值：实际车速*10.比如返回120，实际车速即12.0Km/h     
******************************************************************************/
uint16_t usPROTOCOL_Get_Speed(void)
{
//	if(gucTimeOutError == 0 && (gtParam.Wheel_Diameter == CONFIG_DEF_DIAMETER ) && (CONFIG_SPEIC_PERIMETER != 0)) /*特殊轮径值*/
//    {
//        return 2313U * 600 * gtReceived_Data.Rpm / 1000000;
//    }
//	else 
    if(gucTimeOutError == 0 && gtSend_Data.Wheel_Diameter > 0)
    {
        return 4785U * gtSend_Data.Wheel_Diameter * gtReceived_Data.Rpm / 1000000;
    }
    else
    {
        return 0;
    }
}

/******************************************************************************
** 功  能：获取错误代码
** 参  数：无
** 返回值：十六进制的错误代码
** 备  注：包括30错误代码（通信超时）
**         所有协议通用
******************************************************************************/
uint8_t ucPROTOCOL_Get_Errcode(void)
{
	if(gucTimeOutError == 1)
	{
		return 0x30;
	}
	else
	{
        /*0x01,0x03,0x06,0x22错误代码，仪表不需要显示*/
        if(gtReceived_Data.ErrCode == 0x01 ||gtReceived_Data.ErrCode == 0x03 || gtReceived_Data.ErrCode == 0x06 || gtReceived_Data.ErrCode == 0x22)
        {
            return 0;
        }
        else if((gtReceived_Data.ErrCode >= 0x04 && gtReceived_Data.ErrCode <= 0x09) || (gtReceived_Data.ErrCode >= 0x10 && gtReceived_Data.ErrCode <= 0x15) || (gtReceived_Data.ErrCode >= 0x21 && gtReceived_Data.ErrCode <= 0x26)  ||  (gtReceived_Data.ErrCode == 0x30))
        {
            return gtReceived_Data.ErrCode;
        }
        else
        {
            return 0;
        }
	}
}

/******************************************************************************
** 功  能：工厂指令的应答
** 参  数：Cmd-要应答的指令，Response-要应答的参数
** 返回值：无
** 备  注：
******************************************************************************/
static void vPROTOCOL_Factory_Response(uint8_t Cmd,uint16_t Response)
{
    uint8_t lucRxBuffer[8];

    /*这个函数会在中断中调用。按说不应该采用这种阻塞延时的方式。但是因为校准指令的发送实际不确定，不加延时，*/
    /*有可能造成应答帧和前后发送数据的正常帧连在一起，造成工装无法识别。因此强制增加了20ms的延时。*/
    /*这个函数只是在工厂校准用。正常使用不会调用。因此阻塞延时对用户使用性能无影响*/
    
    vBsp_DelayMs(40);       
    lucRxBuffer[0] = 0x3A;  /*0x3A,0x50是应答帧的帧头*/
    lucRxBuffer[1] = 0x50;
    lucRxBuffer[2] = Cmd;
    lucRxBuffer[3] = Response & 0x00FF;
    lucRxBuffer[4] = (Response >> 8) & 0x00FF;
    lucRxBuffer[5] = lucRxBuffer[2] + lucRxBuffer[3] + lucRxBuffer[4]; /*8位的和校验*/
    lucRxBuffer[6] = 0x0D;
    lucRxBuffer[7] = 0x0A;  /*0x0D,0x0A是固定帧尾*/
    
    USART_MCU_Send_Frame(lucRxBuffer,8);
    vBsp_DelayMs(40);
}

/******************************************************************************
** 功  能：工厂指令的解析
** 参  数：无
** 返回值：无
** 备  注：
******************************************************************************/
void vPROTOCOL_Factory_Command(void)
{
	uint16_t n;
	uint16_t lusCheckSum = 0;
    uint16_t lusResponse;
    
	for(n = 0;n < gusRx_Len - 5;n++)
	{
		lusCheckSum += gucRx_Buffer[1 + n];		/*计算校验值，和校验*/
	}
	if((gucRx_Buffer[3] == gusRx_Len - 8) && (lusCheckSum == gucRx_Buffer[gucRx_Buffer[3] + 5] * 256UL + gucRx_Buffer[gucRx_Buffer[3] + 4]))
	{
		switch(gucRx_Buffer[2])
		{
			case 0x5D:		/*电压校准*/
			{
				lusResponse = vVoltage_Calibration();
                vPROTOCOL_Factory_Response(0x5D,lusResponse);
				break;
			}
			case 0x8E:		/*清除ODO*/
			{
				vClear_ODO();
                vPROTOCOL_Factory_Response(0x8E,5678); /*5678是针对清ODO的固定响应*/
				break;
			}
			case 0x6E:		/*校准光感*/
			{
				lusResponse = vPhotoDiode_Calibration();
                vPROTOCOL_Factory_Response(0x6E,lusResponse); 
				break;
			}
			case 0x71:		/*自动校准时间*/
			{
				vDateTime_Calibration(gucRx_Buffer[7],gucRx_Buffer[8],gucRx_Buffer[9],gucRx_Buffer[4],gucRx_Buffer[5],gucRx_Buffer[6]);
                vPROTOCOL_Factory_Response(0x71,1234); /*1234是针对校准时间的固定响应*/
				break;
			}
			default:break;
		}
	}
}




/******************************************************************************
** 功  能：设置发送运行数据
** 参  数：无
** 返回值：刚刚发送的读请求指令，如果是写数据，则返回0
** 备  注：
******************************************************************************/
static uint8_t ucPROTOCOL_Send_Data(void)
{
	static uint8_t lucIndex = 3;
    static uint8_t lucSubIndex = 0;
    const static uint8_t Pas_3[] = {6,0,12,2,3};
    const static uint8_t Pas_4[] = {6,0,1,12,21,3};
    const static uint8_t Pas_5[] = {6,0,11,13,21,23,3};
    const static uint8_t Pas_9[] = {6,0,1,11,12,13,2,21,22,23,3};
    uint16_t lusRpm;
    uint8_t lucCmd = 0;
    switch(lucIndex)
    {
        case 0:
        {
            gucTx_Buffer[0] = 0x11;             /*读指令的起始字节*/
            gucTx_Buffer[1] = READ_SPEED;       /*读取车速*/
            USART_MCU_Send_Frame(gucTx_Buffer,2);  
            lucCmd = READ_SPEED;
            lucIndex = 1;
            break;
        }
        case 1:
        {
            gucTx_Buffer[0] = 0x11;             /*读指令的起始字节*/
            gucTx_Buffer[1] = READ_CURRENT;     /*读取电流*/
            USART_MCU_Send_Frame(gucTx_Buffer,2);  
            lucCmd = READ_CURRENT;
            lucIndex = 2;
            break;
        }
        case 2:
        {
            gucTx_Buffer[0] = 0x16;             /*写指令的起始字节*/
            gucTx_Buffer[1] = WRITE_PAS;        /*写档位*/
            if(gtSend_Data.Pas_Range == 3)
            {
                gucTx_Buffer[2] = Pas_3[gtSend_Data.Pas + 1];
            }
            else if(gtSend_Data.Pas_Range == 4)
            {
                gucTx_Buffer[2] = Pas_4[gtSend_Data.Pas + 1];
            }
            else if(gtSend_Data.Pas_Range == 5)
            {
                gucTx_Buffer[2] = Pas_5[gtSend_Data.Pas + 1];
            }
            else if(gtSend_Data.Pas_Range == 9)
            {
                gucTx_Buffer[2] = Pas_9[gtSend_Data.Pas + 1];
            }
            else
            {
                gucTx_Buffer[2] = 0;
            }
            gucTx_Buffer[3] = gucTx_Buffer[0] + gucTx_Buffer[1] + gucTx_Buffer[2];
            USART_MCU_Send_Frame(gucTx_Buffer,4);  
            lucCmd = 0x00;/*写指令返回0x00*/
            lucIndex = 3;
            break;
        }
        case 3:
        {
            switch(lucSubIndex)
            {
                case 0:/*读电量*/
                {
                    gucTx_Buffer[0] = 0x11;
                    gucTx_Buffer[1] = READ_SOC;/*读电量*/
                    USART_MCU_Send_Frame(gucTx_Buffer,2);  
                    lucCmd = READ_SOC;
                    lucSubIndex = 1;
                    break;
                }
                case 1:/*读错误代码*/
                {
                    gucTx_Buffer[0] = 0x11;
                    gucTx_Buffer[1] = READ_ERROR;/*读错误代码*/
                    USART_MCU_Send_Frame(gucTx_Buffer,2);
                    lucCmd = READ_ERROR;
                    lucSubIndex = 2;
                    break;
                }
                case 2:/*写限速*/
                {
                    gucTx_Buffer[0] = 0x16;
                    gucTx_Buffer[1] = WRITE_SPEEDLIMIT;/*写限速*/
                    lusRpm = gtSend_Data.Speed_Limit * 10000000U / (4785 * gtSend_Data.Wheel_Diameter);
                    gucTx_Buffer[2] = lusRpm >> 8;
                    gucTx_Buffer[3] = lusRpm & 0x00FF;
                    gucTx_Buffer[4] = gucTx_Buffer[0] + gucTx_Buffer[1] + gucTx_Buffer[2] + gucTx_Buffer[3];
                    USART_MCU_Send_Frame(gucTx_Buffer,5);  
                    lucCmd = 0x00;
                    lucSubIndex = 3;
                    break;
                }
                case 3:/*写大灯状态*/
                {
                    gucTx_Buffer[0] = 0x16;
                    gucTx_Buffer[1] = WRITE_LAMP;/*写大灯状态*/
                    gucTx_Buffer[2] = 0xF0 | gtSend_Data.Lamp;
                    USART_MCU_Send_Frame(gucTx_Buffer,3);
                    lucCmd = 0x00;
                    lucSubIndex = 0;
                    break;
                }
            }
            lucIndex = 0;
            break;
        }
    }
    return lucCmd;
}

/******************************************************************************
** 功  能：开机上电时，设置默认要发送给控制器的数据
** 参  数：无
** 返回值：无
** 备  注：
******************************************************************************/
void vPROTOCOL_Set_Value_When_PowerOn(void)
{
    gtReceived_Data.Soc = -1;/*SOC标志置为-1，表示没有接收到SOC值*/
    
    vPROTOCOL_Set_Wheel(usGet_Wheel_Size(gtParam.Wheel_Diameter));
    vPROTOCOL_Set_Pas_Range(gtParam.Pas_Max);
    vPROTOCOL_Set_Pas(0);/*开机LOGO界面发0档*/
    vPROTOCOL_Set_Speed_Limit(gtParam.Speed_Limit);
    vPROTOCOL_Send_Run_Data();
}

/******************************************************************************
** 功  能：复位通信超时计数变量
** 参  数：无
** 返回值：无
** 备  注：
******************************************************************************/
void vPROTOCOL_ResetCommunicationOverTime(void)
{
    __disable_irq();
    gucTimeOutError = 0;
    gulCommunicationCnt = ulBsp_Getticks();
    __enable_irq();
    
}

/******************************************************************************
** 功  能：退出设置时，设置默认要发送给控制器的数据
** 参  数：无
** 返回值：无
** 备  注：
******************************************************************************/
void vPROTOCOL_Set_Value_When_ExitSetting(void)
{
    vPROTOCOL_Set_Wheel(usGet_Wheel_Size(gtParam.Wheel_Diameter));
	vPROTOCOL_Set_Speed_Limit(gtParam.Speed_Limit);
}

